#include "scene.hpp"

#include <iostream>
using namespace std;

Scene::Scene() 
{
	SDL.SetSurface(&screen,Defined::width,Defined::height); 
}

Color Scene::PhotoExposure(Color& srcCol)
{
	double exposure=-1.00;
	Color resCol;
	resCol.SetRed(1.0-exp(srcCol.GetRed()*exposure));
	resCol.SetBlue(1.0-exp(srcCol.GetBlue()*exposure));
	resCol.SetGreen(1.0-exp(srcCol.GetGreen()*exposure));
	return resCol;
		
}

bool Scene::isShadowed(Ray& lightRay,double distToLight, double& dist)
{
	bool inShadow=false;
	for (unsigned int i=0; i<objectsList.size(); i++)
	{
		if ((objectsList[i])->Intersect(lightRay, dist))
			if ((dist < distToLight)&&(objectsList[i]!=nearestObject)) inShadow=true;
	}
	return inShadow;
	
}

void Scene::SaveScreenshot(SDL_Surface * screen)
{
	using namespace Defined;
	Time myTime;
	string fileName=string(myTime.GetDateAndTime());
	fileName=PREFIX+fileName+EXTENSION;

	if (SDL.SaveSurface(screen, fileName.c_str())) cout << "Screenshot: "<<fileName<<endl;
	else cout <<"Screenshot: FAILED" << endl;
}

void Scene::DoMainCycle(SDL_Surface * screen)
{
	using namespace Defined;
	//main cycle	

	for (int x=0; x<width; x++)
		for (int y=0; y<height; y++)
		{
			Color tempColor;
			double minDist;
			//Antialising code. Start////////////////
			for (double fragmentX = x; fragmentX<x+1.0f; fragmentX+=1.0/sqrt(double(AntiAlising)))
				for (double fragmentY = y; fragmentY<y+1.0f; fragmentY+=1.0/sqrt(double(AntiAlising)))
			{
				Point3D origin(width/2,height/2,fieldOfView); //start of a ray
				Point3D ending(fragmentX,fragmentY,0); //end of a ray
				Vector3D direction=ending-origin; 
				Ray viewRay(origin,normalize(direction)); //creating of a ray
	
				int level=0;
				double coef=1.0;
				bool intersectionExist=false;
				
				do //cycle for reflections
				{
				intersectionExist=false;
				if (FindNearestIntersection(viewRay, minDist))
				{
					intersectionExist=true;
					Vector3D intersection=viewRay.startPoint + minDist*viewRay.direction; 
					
					for (unsigned int i=0; i<lightsList.size(); i++)
					{
						Vector3D lightDirection = lightsList[i]->position-intersection;   
				
						Ray lightRay(ConvertToPoint(intersection),normalize(lightDirection)); 
					
						double distToLight = sqrt(lightDirection&lightDirection); 				
						double distToObject=FLT_MAX;
				
						if (!isShadowed(lightRay, distToLight, distToObject)) 
						{
							Color tracedCol=nearestObject->Trace(viewRay, lightsList[i]->position ,minDist);
							
							Color lightCol=lightsList[i]->lightColor;
							double colIntense=lightsList[i]->intense;
							lightCol *= colIntense;
							tempColor += lightCol*tracedCol;
						}
						// We iterate on the next reflection
					}
						Material tracedObjMat = nearestObject -> GetMaterial(); 
						coef *= tracedObjMat.reflect;
						
						Vector3D n = nearestObject -> GetNormalVect();
						
						double reflet = 2.0f * (viewRay.direction & n);
						viewRay.startPoint = ConvertToPoint(intersection);
						viewRay.direction = viewRay.direction - (n*reflet);	
						level++;			
					
				}
							
				} while ((coef > GeomThreshold) && (level < TraceDepth) && (intersectionExist)); 
				
				Color resCol=tempColor*(1.0/float(AntiAlising));
				resCol = PhotoExposure(resCol);
				
				SDL.DrawPixel(screen, Point(x,y,resCol));					
			}
		}
}

void Scene::Render()
{
	SDL.LockSurface(screen);
	DoMainCycle(screen);
	SDL.UnlockSurface(screen);
	SDL.Flip(screen);
	SaveScreenshot(screen);
}
